View Javadoc
1 /* 2 * Scope: a generic MVC framework. 3 * Copyright (c) 2000-2002, The Scope team 4 * All rights reserved. 5 * 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 11 * Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 14 * Redistributions in binary form must reproduce the above copyright 15 * notice, this list of conditions and the following disclaimer in the 16 * documentation and/or other materials provided with the distribution. 17 * 18 * Neither the name "Scope" nor the names of its contributors 19 * may be used to endorse or promote products derived from this software 20 * without specific prior written permission. 21 * 22 * 23 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 24 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 25 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 26 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR 27 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 28 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 29 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 30 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 31 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 32 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 33 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 34 * 35 * 36 * $Id: STextField.java,v 1.17 2002/09/13 17:04:42 ludovicc Exp $ 37 */ 38 package org.scopemvc.view.swing; 39 40 41 import java.awt.event.ActionEvent; 42 import java.awt.event.ActionListener; 43 import java.awt.event.FocusEvent; 44 import java.awt.event.FocusListener; 45 import java.beans.Beans; 46 import javax.swing.JTextField; 47 import javax.swing.JToolTip; 48 import org.apache.commons.logging.Log; 49 import org.apache.commons.logging.LogFactory; 50 import org.scopemvc.core.Control; 51 import org.scopemvc.core.Controller; 52 import org.scopemvc.core.PropertyView; 53 import org.scopemvc.core.Selector; 54 import org.scopemvc.util.Debug; 55 import org.scopemvc.util.convertor.StringConvertor; 56 import org.scopemvc.util.convertor.StringConvertors; 57 import org.scopemvc.view.util.ModelBindable; 58 59 /*** 60 * <P> 61 * 62 * A JTextField linked to a property of a bound model object. The property must 63 * have a StringConvertor to handle conversion to and from a String 64 * representation that will be edited in the textfield. Updates to the textfield 65 * result in changes to the model property when focus is lost. </P> <P> 66 * 67 * STextField responds to the bound model or the particular bound property 68 * becoming read-only by disabling itself. An STextField is also disabled if it 69 * has no bound model or property. </P> <P> 70 * 71 * STextField can issue a Control when the user hits the Enter key. </P> <P> 72 * 73 * Null properties are handled in one of two ways: 74 * <OL> 75 * <LI> The field is disabled to prevent editing. </LI> 76 * <LI> The field is populated with an empty String. In this case, the bound 77 * property will contain an empty String not a null, if the textfield is 78 * populated with an empty string. </LI> 79 * </OL> 80 * The second option is the default but that can be changed by calling {@link 81 * #setDisableOnNull}. </P> 82 * 83 * @author <A HREF="mailto:smeyfroi@users.sourceforge.net">Steve Meyfroidt</A> 84 * @created 05 September 2002 85 * @version $Revision: 1.17 $ $Date: 2002/09/13 17:04:42 $ 86 * @see SwingView 87 */ 88 public class STextField extends JTextField implements PropertyView, FocusListener, ActionListener, ModelBindable, Refreshable { 89 90 private static final Log LOG = LogFactory.getLog(STextField.class); 91 92 /*** 93 * Helper to manage model to view binding. 94 */ 95 private SwingBoundModel boundModel = new SwingBoundModel(this); 96 97 /*** 98 * Helper to manage validation state. 99 */ 100 private ValidationHelper validationHelper = new ValidationHelper(this); 101 102 /*** 103 * ID of the Control issued when user hits Enter in the STextField. 104 */ 105 private String controlID; 106 107 /*** 108 * STextField can "hold" a null when bound to a property that happens to be 109 * null. 110 */ 111 private boolean valueIsNull = false; 112 113 /*** 114 * Does this textfield disable itself if the model property it is bound to 115 * becomes null? 116 */ 117 private boolean disableOnNull = false; 118 119 /*** 120 * The StringConvertor used to convert the model property to and from the 121 * String representation that the user edits in the textfield. 122 */ 123 private StringConvertor stringConvertor; 124 125 /*** 126 * If this is set, use it instead of finding a convertor to match the 127 * datatype being edited. 128 */ 129 private StringConvertor forcedStringConvertor; 130 131 132 /*** 133 * Constructor for the STextField object 134 */ 135 public STextField() { 136 this(12); 137 } 138 139 140 /*** 141 * Constructor for the STextField object 142 * 143 * @param columns TODO: Describe the Parameter 144 */ 145 public STextField(int columns) { 146 super(columns); 147 setEnabled(Beans.isDesignTime()); 148 addFocusListener(this); 149 addActionListener(this); 150 } 151 152 153 /*** 154 * @return true if the textfield should be disabled when it edits a null 155 * property value. Otherwise, a null property is treated as an empty 156 * String. 157 */ 158 public final boolean isDisableOnNull() { 159 return disableOnNull; 160 } 161 162 163 // ------------------- Delegate to BoundModel ------------------- 164 165 /*** 166 * Gets the bound model 167 * 168 * @return The boundModel value 169 */ 170 public final Object getBoundModel() { 171 return boundModel.getBoundModel(); 172 } 173 174 175 /*** 176 * Gets the selector 177 * 178 * @return The selector value 179 */ 180 public final Selector getSelector() { 181 return boundModel.getSelector(); 182 } 183 184 185 // ---------- Issue a Control when user hits Enter in the STextfield -------------- 186 187 /*** 188 * Gets the control ID 189 * 190 * @return The controlID value 191 */ 192 public final String getControlID() { 193 return controlID; 194 } 195 196 197 /*** 198 * Get the current value (what would be set as a property of the bound model 199 * object) being presented on the View. 200 * 201 * @return property value from parsing the textfield's current String 202 * representation. 203 * @exception IllegalArgumentException if the conversion from String fails. 204 */ 205 public Object getViewValue() throws IllegalArgumentException { 206 String text = getText(); 207 208 if (stringConvertor == null 209 || valueIsNull 210 || (isDisableOnNull() && text.length() < 1)) { 211 return null; 212 } 213 214 return stringConvertor.stringAsValue(text); 215 } 216 217 218 /*** 219 * Don't assign a Controller to STextField, instead delegate to the 220 * containing SwingView that has a parent Controller. 221 * 222 * @return The controller value 223 */ 224 public Controller getController() { 225 return null; 226 } 227 228 229 /*** 230 * Don't assign a Controller to STextField, instead delegate to the 231 * containing SwingView that has a parent Controller. 232 * 233 * @param inControl TODO: Describe the Parameter 234 */ 235 public void issueControl(Control inControl) { 236 SwingUtil.issueControl(this, inControl); 237 } 238 239 240 /*** 241 * Set this to true if the textfield should be disabled when it edits a null 242 * property value. Otherwise, a null property is treated as an empty String. 243 * 244 * @param inDisable The new disableOnNull value 245 */ 246 public final void setDisableOnNull(boolean inDisable) { 247 disableOnNull = inDisable; 248 } 249 250 251 /*** 252 * Sets the bound model 253 * 254 * @param inModel The new boundModel value 255 */ 256 public final void setBoundModel(Object inModel) { 257 boundModel.setBoundModel(inModel); 258 setupStringConvertor(); 259 } 260 261 262 /*** 263 * Sets the selector 264 * 265 * @param inSelector The new selector value 266 */ 267 public final void setSelector(Selector inSelector) { 268 boundModel.setSelector(inSelector); 269 setupStringConvertor(); 270 } 271 272 273 /*** 274 * Sets the selector string 275 * 276 * @param inSelectorString The new selectorString value 277 */ 278 public final void setSelectorString(String inSelectorString) { 279 boundModel.setSelectorString(inSelectorString); 280 setupStringConvertor(); 281 } 282 283 /*** 284 * Set the ID of the Control that will be issued when Enter key is pressed 285 * in this STextField. If null no Control will be issued. 286 * 287 * @param inControlID The new controlID value 288 */ 289 public final void setControlID(String inControlID) { 290 controlID = inControlID; 291 } 292 293 294 /*** 295 * Force use of this StringConvertor instead of automatically finding one to 296 * match the datatype being edited. 297 * 298 * @param inConvertor The new stringConvertor value 299 */ 300 public void setStringConvertor(StringConvertor inConvertor) { 301 forcedStringConvertor = inConvertor; 302 } 303 304 305 /*** 306 * Override to call super.setText() only if new value not equals() old 307 * value. 308 * 309 * @param t new text. 310 */ 311 public void setText(String t) { 312 if (Debug.ON) { 313 Debug.assertTrue(getText() != null, "null getText()"); 314 } 315 if (!getText().equals(t)) { 316 super.setText(t); 317 } 318 setCaretPosition(0); 319 } 320 321 /*** 322 * Don't assign a Controller to this component, instead delegate to the 323 * containing SwingView that has a parent Controller. 324 * 325 * @param inController The new controller value 326 */ 327 public void setController(Controller inController) { 328 throw new UnsupportedOperationException("Can't assign a Controller to a " + getClass()); 329 } 330 331 // --------------------- Implement ModelBindable ---------------------- 332 333 /*** 334 * Converts the incoming value to a String via appropriate {@link 335 * org.scopemvc.util.convertor.StringConvertor}. For incoming null either 336 * disable field or set text to empty String. 337 * 338 * @param inValue TODO: Describe the Parameter 339 * @param inReadOnly TODO: Describe the Parameter 340 */ 341 public void updateFromProperty(Object inValue, boolean inReadOnly) { 342 if (LOG.isDebugEnabled()) { 343 LOG.debug("updateFromProperty: " + inValue + ", " + inReadOnly); 344 } 345 346 // ***** Hack. Need to revisit PropertyManager to traverse nulls and use metadata etc 347 setupStringConvertor(); 348 349 if (stringConvertor == null) { 350 valueIsNull = true; 351 // ? 352 setEnabled(false); 353 setText(""); 354 return; 355 } 356 357 if (inValue == null && isDisableOnNull()) { 358 valueIsNull = true; 359 setEnabled(false); 360 setText(""); 361 return; 362 } 363 364 valueIsNull = false; 365 //(inValue == null); 366 setEnabled(!inReadOnly); 367 try { 368 String text = stringConvertor.valueAsString(inValue); 369 setText(text); 370 } catch (IllegalArgumentException e) { 371 // should never happen normally -- comes from getValue() but the 372 // ... property value must always be convertible to String? 373 LOG.error("updateFromProperty", e); 374 setEnabled(false); 375 } 376 } 377 378 379 /*** 380 * TODO: document the method 381 * 382 * @param inException TODO: Describe the Parameter 383 */ 384 public void validationFailed(Exception inException) { 385 validationHelper.validationFailed(inException); 386 } 387 388 389 /*** 390 * TODO: document the method 391 */ 392 public void validationSuccess() { 393 validationHelper.validationSuccess(); 394 } 395 396 397 /*** 398 * TODO: document the method 399 * 400 * @return TODO: Describe the Return Value 401 */ 402 public JToolTip createToolTip() { 403 return validationHelper.createToolTip(super.createToolTip()); 404 } 405 406 407 // ---------------------- View to model ---------------------- 408 409 /*** 410 * TODO: document the method 411 * 412 * @param inEvent TODO: Describe the Parameter 413 */ 414 public void focusLost(FocusEvent inEvent) { 415 if (LOG.isDebugEnabled()) { 416 LOG.debug("focusLost"); 417 } 418 419 if (!inEvent.isTemporary()) { 420 boundModel.updateModel(); 421 if (controlID != null) { 422 Control control = new Control(controlID); 423 issueControl(control); 424 } 425 } 426 } 427 428 429 /*** 430 * TODO: document the method 431 * 432 * @param inEvent TODO: Describe the Parameter 433 */ 434 public void focusGained(FocusEvent inEvent) { } 435 436 437 /*** 438 * Sync model object to View and issue a Control if ControlID set. 439 * 440 * @param e TODO: Describe the Parameter 441 */ 442 public void actionPerformed(ActionEvent e) { 443 boundModel.updateModel(); 444 // ... then do the sync to model 445 446 if (controlID != null) { 447 Control control = new Control(controlID); 448 issueControl(control); 449 } 450 } 451 452 453 // ------------------ Refreshable ------------------------- 454 455 /*** 456 * TODO: document the method 457 */ 458 public void refresh() { 459 Object propertyValue = boundModel.getPropertyValue(); 460 boolean propertyReadOnly = boundModel.getPropertyReadOnly(); 461 updateFromProperty(propertyValue, propertyReadOnly); 462 } 463 464 465 /*** 466 * TODO: document the method 467 */ 468 protected void setupStringConvertor() { 469 if (forcedStringConvertor != null) { 470 stringConvertor = forcedStringConvertor; 471 return; 472 } 473 474 try { 475 Object m = getBoundModel(); 476 Selector s = getSelector(); 477 if (m != null) { 478 Class clazz = boundModel.getPropertyManager().getPropertyClass(m, s); 479 stringConvertor = StringConvertors.forClass(clazz); 480 } 481 } catch (Exception e) { 482 LOG.warn("setupStringConvertor", e); 483 stringConvertor = null; 484 } 485 } 486 } 487

This page was automatically generated by Maven